use std::{path::{Path, PathBuf}, process::exit
};

use chrono::Local;
use clap::Parser;
use libsm::sm4::{self, Mode};
use ssh::Server;
use base64::prelude::*;

mod ssh;
mod xlsx;

const SM4_KEY: &str = "D12NkCxycXsHcNjb";
const SM4_IV: &str = "ihWAEd74BdH0iT6F";

#[derive(Parser, Clone, Debug)]
struct Opt {
    /// Unix SSH Host
    #[arg(short = 'H', long, env = "SCP2_HOST")]
    host: Option<String>,

    /// Unix SSH Username
    #[arg(short, long, env = "SCP2_USER")]
    username: Option<String>,

    /// Unix SSH User Password
    #[arg(short, long, env = "SCP2_PASSWORD")]
    password: Option<String>,

    /// Unix SSH Port
    #[arg(short = 'P', long, env = "SCP2_PORT", default_value = "22")]
    port: String,

    /// Only supports a single command
    #[arg(short, long, env = "SCP2_COMMAND")]
    command: Option<String>,

    /// Only supports a single command
    #[arg(short, long, env = "SCP2_BEFORE_COMMAND")]
    before_command: Option<String>,

    #[arg(short, long, env = "SCP2_AFTER_COMMAND")]
    after_command: Option<String>,

    /// Print Command or Upload Elapsed.
    #[arg(short, long, default_value = "false")]
    timing: bool,

    /// Get file from remove server?
    #[arg(short, long, default_value = "false")]
    get: bool,

    // #[arg(long, default_value = "false")]
    // sm4: bool,
    #[arg(long)]
    sm4_key: Option<String>,
    #[arg(long)]
    sm4_iv: Option<String>,
    #[arg(long, default_value = "false")]
    sm4_encrypt_only: bool,
    #[arg(long, default_value = "false")]
    sm4_decrypt_only: bool,

    #[arg(short='D', long, default_value = "false")]
    sm4_decrypt: bool,

    ///
    #[arg(short = 'x', long)]
    xlsxfile: Option<PathBuf>,
    #[arg(short = 'N', long)]
    xlsx_start: Option<usize>,

    /// Localfile Remotefile
    file_list_or_command: Vec<String>,
}

fn decrypt(key: String, iv: String, input: String) -> String {
    let sm4 = sm4::Cipher::new(key.as_bytes(), Mode::Cbc).unwrap();
    let data = BASE64_STANDARD.decode(input).unwrap();
    let decrypt = match sm4.decrypt(&[], &data, iv.as_bytes()) {
        Err(e) => {
            eprintln!("Decryption failed: {e}");
            exit(1);
        },
        Ok(decrypt) => decrypt,
    };
    String::from_utf8_lossy(&decrypt).to_string()
}

fn encrypt(key: String, iv: String, input: String) -> String {
    let sm4 = sm4::Cipher::new(key.as_bytes(), Mode::Cbc).unwrap();
    let encrypt = match sm4.encrypt(&[], &input.as_bytes(), iv.as_bytes()) {
        Err(e) => {
            eprintln!("Encryption failed: {e}");
            exit(1);
        },
        Ok(encrypt) => encrypt,
    };
    BASE64_STANDARD.encode(encrypt)
}

fn main() {

    let opt = Opt::parse();
    let mut passwd = opt.password.unwrap_or_default();
    if opt.sm4_decrypt_only {
        println!("{}", decrypt(opt.sm4_key.unwrap_or(SM4_KEY.to_owned()), opt.sm4_iv.unwrap_or(SM4_IV.to_owned()), passwd));
        exit(0);
    } else if opt.sm4_encrypt_only {
        println!("{}", encrypt(opt.sm4_key.unwrap_or(SM4_KEY.to_owned()), opt.sm4_iv.unwrap_or(SM4_IV.to_owned()), passwd));
        exit(0);
    }

    if opt.sm4_decrypt {
        passwd = decrypt(opt.sm4_key.unwrap_or(SM4_KEY.to_owned()), opt.sm4_iv.unwrap_or(SM4_IV.to_owned()), passwd);
    }

    if let Some(file) = opt.xlsxfile {
        xlsx::xls2txt(file, opt.xlsx_start);
        return;
    }

    let mut s = Server::default();
    if let Some(host) = opt.host {
        s.hostname = host;
    } else {
        panic!("--host: Parameter undefined");
    }
    s.password = passwd.clone();

    if let Some(username) = opt.username {
        s.username = username;
    } else {
        panic!("--username: Parameter undefined");
    }
    s.port = opt.port.clone();

    let sess = crate::ssh::connect_ssh(&s);

    // before
    if let Some(cmd) = opt.before_command {
        crate::ssh::exec_cmd(&sess, &cmd, opt.timing);
    }

    if let Some(cmd) = opt.command {
        crate::ssh::exec_cmd(&sess, &cmd, opt.timing);
    } else {
        let mut files = opt.file_list_or_command;
        match files.len() {
            0 => {
                println!(
                    "{} [OPTIONS] file list ... or single-command",
                    crate::ssh::PACKAGE_NAME
                );
                exit(1);
            }
            1 => {
                let cmd = files.pop().unwrap();
                crate::ssh::exec_cmd(&sess, &cmd, opt.timing);
                return;
            }
            _ => {}
        }

        let remote_file = files.pop().unwrap().to_owned();

        for file in files {
            // println!("file: {}, remote_file: {}", file.display(), remote_file.clone().display());
            let now = Local::now();
            if !opt.get {
                crate::ssh::scp_send(
                    &sess,
                    s.clone(),
                    Path::new(&file).to_path_buf(),
                    Path::new(&remote_file).to_path_buf(),
                );
            } else {
                // 下载文件
                let local_file = &remote_file;
                let remote_file = file.as_str();

                crate::ssh::scp_recv(
                    &sess,
                    s.clone(),
                    Path::new(local_file).to_path_buf(),
                    Path::new(remote_file).to_path_buf(),
                );
            }

            if opt.timing {
                let now = Local::now() - now;
                println!(
                    "Elapsed: {:.3} sec",
                    (now.num_milliseconds() as f64 / 1000.0) as f64
                );
            }
        }
    }

    // after
    if let Some(cmd) = opt.after_command {
        crate::ssh::exec_cmd(&sess, &cmd, opt.timing);
    }

    println!("Connection closed")
}

// #[cfg(test)]
// mod tests {
//     use std::{thread::sleep, time::Duration};

//     #[test]
//     fn test(){

//         use indicatif::{ProgressBar, ProgressStyle};

// let bar = ProgressBar::new(100);
// // bar.set_style(
// //     ProgressStyle::default_bar()
// //         .template("{spinner:.cyan} [{bar:40.cyan/blue}] {pos}/{len} ({eta})").unwrap() // 自定义模板
// //         .progress_chars("=> ") // 进度条符号
// //         .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ") // Spinner 动画符号
// // );
// bar.set_style(
//     ProgressStyle::default_bar()
//         .template("{msg} {spinner:.green} {wide_bar:.red/yellow} {percent}% ({eta})").unwrap()
//         .progress_chars("█▓▒░") // 渐变符号
// );
// bar.set_message("正在处理数据..."); // 动态消息

//         for _ in 0..100 {
//             bar.inc(1); // 更新进度
//             sleep(Duration::from_secs(1));
//         }
//         bar.finish_with_message("完成"); // 结束并显示消息

//     }

//     #[test]
//     fn test2() {
//         use pbr::ProgressBar;
//         let mut pb = ProgressBar::new(100);
//     pb.format("╢▌▌░╟"); // 自定义进度条符号
//     for _ in 0..100 {
//         pb.inc();
//         sleep(Duration::from_secs(1));
//     }
//     pb.finish_print("处理完毕");
//     }

//     #[test]
//     fn test3() -> std::io::Result<()> {
//         use pbr::ProgressBar;
// use std::fs::File;
// use std::io::{Read, Write};

//         let mut src = File::open("/Users/lbk/Downloads/instantclient-basic-macos.x64-19.16.0.0.0dbru.dmg")?;
//         let mut dst = File::create("output.bin")?;
//         let file_size = src.metadata()?.len() as usize;

//         let mut pb = ProgressBar::new(file_size as u64);
//         pb.set_units(pbr::Units::Bytes); // 显示字节单位:ml-citation{ref="1,5" data="citationList"}

//         let mut buffer = vec![0u8; 4096];
//         loop {
//             let bytes_read = src.read(&mut buffer)?;
//             if bytes_read == 0 {
//                 break;
//             }
//             dst.write_all(&buffer[..bytes_read])?;
//             pb.add(bytes_read as u64); // 根据实际写入量更新进度:ml-citation{ref="2,5" data="citationList"}
//         }
//         pb.finish_print("/Users/lbk/Downloads/instantclient-basic-macos.x64-19.16.0.0.0dbru.dmg OK");
//         Ok(())
//     }
// }
